﻿using System;
using System.Collections.Generic;
using System.Reflection;
using NsbExtensions.Endpoint.Management;
using NsbExtensions.Endpoint.Management.Monitoring;
using NsbExtensions.Messages;
using ObjectBuilder;

namespace NsbExtensions.Configuration
{
    public class EndpointConfigurationManager
    {
        public static IComponentConfig ConfigureEndpoint<T>(IBuilder builder) where T : IMessagingEndpoint
        {
            IComponentConfig builderConfig = builder.ConfigureComponent(typeof (T), ComponentCallModelEnum.Singleton);

            EndpointConfiguration config = EndpointConfiguration.GetConfiguration();
            if (config != null)
            {
                builderConfig.ConfigureProperty("Metadata", config.CreateEndpointDescription());

                var messageHandlerAssemblies = new List<Assembly>(config.MessageHandlerAssemblies.Count)
                                                   {
                                                       typeof (MessagingEndpoint).Assembly,
                                                       typeof (EndpointMessage).Assembly
                                                   };

                for (int i = 0; i < config.MessageHandlerAssemblies.Count; i++)
                {
                    AssemblyConfigurationElement asm = config.MessageHandlerAssemblies[i];
                    if (asm.Assembly != null)
                        messageHandlerAssemblies.Add(asm.Assembly);
                }

                builderConfig.ConfigureProperty("MessageHandlerAssemblies", messageHandlerAssemblies);
            }

            return builderConfig;
        }

        public static IComponentConfig ConfigureManagedEndpoint<EndpointType, ManagerType>(IBuilder builder)
            where EndpointType : ManagedEndpoint where ManagerType : IEndpointManagementClientService
        {
            IComponentConfig builderConfig = ConfigureEndpoint<EndpointType>(builder);
            IComponentConfig managerConfig = builder.ConfigureComponent(typeof (ManagerType),
                                                                        ComponentCallModelEnum.Singleton);


            //The configuration file takes precedence over all code-based configuration
            EndpointManagerConfiguration epmConfig = EndpointManagerConfiguration.GetConfiguration();
            if (epmConfig != null)
            {
                //Create an empty list of probes we will later fill (sounds dirty, huh?)
                var statusProbes = new List<IEndpointStatusProbe>();

                if (epmConfig.StatusProbes != null)
                {
                    foreach (EndpointStatusProbeConfigurationElement probeConfig in epmConfig.StatusProbes)
                    {
                        IEndpointStatusProbe probe = probeConfig.CreateProbeFromConfiguration();
                        if (probe != null)
                            statusProbes.Add(probe);
                    }
                }

                managerConfig
                    .ConfigureProperty("StatusReportInterval", epmConfig.StatusReportInterval)
                    .ConfigureProperty("StatusReportGracePeriod", epmConfig.StatusReportGracePeriod)
                    .ConfigureProperty("MonitorServiceAddress", epmConfig.MonitorServiceAddress)
                    .ConfigureProperty("EndpointStatusProbes", statusProbes);
            }

            return builderConfig;
        }

        /// <summary>
        /// As strange as this seems, it proved necessary
        /// during certain scenarios to ensure that the 
        /// singleton IBuilder that will be injected by the IoC
        /// is the exact same instance of the original IBuilder
        /// used to configure the endpoint & supporting objects.
        /// </summary>
        /// <returns></returns>
        public static IBuilder CreateBuilder<BuilderType>() where BuilderType : IBuilder
        {
            var builder = (IBuilder) Activator.CreateInstance(typeof (BuilderType));
            return builder.Build<IBuilder>();
        }
    }
}